package cate.game.role.farm;


import cate.common.table.d.GD;
import cate.common.table.d.GDChallenge;
import cate.common.table.farm.FightChallenge;
import cate.common.table.farm.OperOption;
import cate.common.table.item.hero.row.HeroBaseRow;
import cate.game.GameBody;
import cate.game.play.fighter.Fighter;
import cate.game.play.fighter.FighterPerformance;
import cate.game.play.fighter.HeroFighter;
import cate.game.play.part.BattleHero;
import cate.game.play.proce.play.PlayPO;
import easy.java.dev.note.NoteClass;

import java.util.*;
import java.util.stream.Collectors;

@NoteClass(value = "战斗挑战检测")
public class FightChallengeHandler {

    public static boolean checkDone(GameBody game, PlayPO play, FightChallenge row) {
        if (play == null || row == null) {
            return false;
        }
        List<OperOption> options = row.getOperOptions();
        for(OperOption option : options){
            // 根据不同的挑战类型获取当前值
            long value = getValue(game, play, row.getType(),row.getHeroProto(), option);
            // 用当前值和目标值进行比较
            switch (option.checkOper) {
                case GDChallenge.CheckOper.EQ:
                    if(value != option.value){
                        return false;
                    }
                    break;
                case GDChallenge.CheckOper.LTE:
                    if(value > option.value){
                        return false;
                    }
                    break;
                case GDChallenge.CheckOper.GTE:
                    if(value < option.value){
                        return false;
                    }
                    break;
                default:
                    return false;
            }
        }
        return true;
    }

    private static long getValue(GameBody game, PlayPO play, int type,int heroProtoId, OperOption option) {
        switch (type) {
            case GDChallenge.Type.WIN:
                return 1;
            case GDChallenge.Type.POSITION:
                return option.param == play.a.positionTid ? 1 : 0;
            case GDChallenge.Type.ROUND:
                return play.rounds.size();
            case GDChallenge.Type.JOB:
                return getNumbByJob(game, play, option.param);
            case GDChallenge.Type.CAMP:
                return getNumbByCamp(game, play, option.param);
            case GDChallenge.Type.HERO_PROTOID_REQUIRED:
                return heroProtoIdRequired(game, play, option.param);
            case GDChallenge.Type.HERO_PROTOID_BAN:
                return heroProtoIdBan(game, play, option.param);
            case GDChallenge.Type.HERO_DEATH:
                return getNumbByHeroDeath(play);
            case GDChallenge.Type.HP_PERCENT:
                return getHpPercent(play, option.checkOper);
            case GDChallenge.Type.HERO_STAR_NUM:
                return getNumbByStar(game, play, option.param);
            case GDChallenge.Type.HERO_LEVEL_HIGHER_THAN:
                return getHeroLevelHigherThan(play, option.param);
            case GDChallenge.Type.HERO_LEVEL_LOWER_THAN:
                return getHeroLevelLowerThan(play, option.param);
            case GDChallenge.Type.TOTAL_DAMAGE:
                return getTotalDamage(play);
            case GDChallenge.Type.TOTAL_HEAL:
                return getTotalHeal(play);
            case GDChallenge.Type.TOTAL_HURT:
                return getTotalHurt(play);
            case GDChallenge.Type.HERO_STAR:
                return getHeroStar(game, play, heroProtoId);
            case GDChallenge.Type.HERO_SKILL_NUM:
                return getHeroSkillNum(game, play, heroProtoId, option.param);
            case GDChallenge.Type.HERO_HURT_PERCENT:
                return getHurtPercent(play, heroProtoId);
            case GDChallenge.Type.HERO_DAMAGE_PERCENT:
                return getDamagePercent(play, heroProtoId);
            case GDChallenge.Type.HERO_HEAL_PERCENT:
                return getHealPercent(play, heroProtoId);
            case GDChallenge.Type.HERO_KILL_NUM:
                return getKillNum(play,heroProtoId);
            case GDChallenge.Type.HERO_DEAD_NUM:
                return getDeadNum(play, heroProtoId);
            case GDChallenge.Type.POSITION_HERO_NUM:
                return getPosHeroNum(play);
            case GDChallenge.Type.TEAM_ROUND_KILL:
                return getTeamRoundKill(play, option.param);
            case GDChallenge.Type.BUFF_ADD_NUM:
                return getBuffAddNum(play,heroProtoId, option.param);
            case GDChallenge.Type.ADD_BUFF_NUM_ONE_ROUND:
                return getBuffAddNumOneRound(play,heroProtoId, option.param);
            case GDChallenge.Type.STRIKE_NUM:
                return getStrikeNum(play,heroProtoId);
            case GDChallenge.Type.STRIKE_NUM_ONE_ROUND:
                return getStrikeNumOneRound(play, heroProtoId);
            case GDChallenge.Type.CAST_SKILL_NUM:
                return getCastSkillNum(play,heroProtoId,option.param);
            case GDChallenge.Type.FIRST_KILL_ROW_NUM:
                return getFirstKillRowNum(play,heroProtoId,option.param);
            default:
                return 0L;
        }
    }

    private static int getNumbByJob(GameBody game, PlayPO play, int job) {
        int value = 0;
        for (BattleHero hero : play.a.hs) {
            if (hero.hero.getTpl(game.fight.getPlayBuildContext()).job == job) {
                value++;
            }
        }
        return value;
    }

    private static int getNumbByCamp(GameBody game, PlayPO play, int camp) {
        int value = 0;
        for (BattleHero hero : play.a.hs) {
            if (hero.hero.getTpl(game.fight.getPlayBuildContext()).camp == camp) {
                value++;
            }
        }
        return value;
    }

    private static int getNumbByStar(GameBody game, PlayPO play, int star) {
        int value = 0;
        for (BattleHero hero : play.a.hs) {
            if (hero.hero.getTpl(game.fight.getPlayBuildContext()).star >= star) {
                value++;
            }
        }
        return value;
    }

    private static int getHeroLevelHigherThan(PlayPO play, int level) {
        int value = 0;
        for (BattleHero hero : play.a.hs) {
            if (Math.max(hero.hero.level, hero.hero.shareLevel) > level) {
                value++;
            }
        }
        return value;
    }

    private static int getHeroLevelLowerThan(PlayPO play, int level) {
        int value = 0;
        for (BattleHero hero : play.a.hs) {
            if (Math.max(hero.hero.level, hero.hero.shareLevel) < level) {
                value++;
            }
        }
        return value;
    }

    private static int heroProtoIdRequired(GameBody game, PlayPO play, int protoId) {
        int value = 0;
        Set<Integer> fightProtoIds =
                play.a.hs.stream().map(a -> a.hero.getTpl(game.fight.getPlayBuildContext()).protoId).collect(Collectors.toSet());
        if (fightProtoIds.contains(protoId)) {
            value = 1;
        }
        return value;
    }

    private static int heroProtoIdBan(GameBody game, PlayPO play, int protoId) {
        for (BattleHero h : play.a.hs) {
            if (protoId == h.hero.getTpl(game.fight.getPlayBuildContext()).protoId) {
                return 0;
            }
        }
        return 1;
    }

    private static int getNumbByHeroDeath(PlayPO play) {
        int value = 0;
        for (HeroFighter hero : play.teamEnd.a.getAllHero()) {
            if (hero.isDead()) {
                value++;
            }
        }
        return value;
    }

    private static int getHpPercent(PlayPO play, byte checkOper) {
        Set<Integer> set = new HashSet<>();
        for (HeroFighter hero : play.teamEnd.a.getAllHero()) {
            if(hero.isDead()){
                return 0;
            }
            set.add((int) (hero.attr.total().hpCurRate() / 100));
        }
        // 存活英雄血量百分比都大于50 -> 存活英雄最少的血量百分比大于50
        if (checkOper == GDChallenge.CheckOper.GTE) {
            return Collections.min(set);
        }
        return Collections.max(set);
    }

    private static long getTotalDamage(PlayPO play) {
        long totalDamage = 0L;
        for (HeroFighter hero : play.teamEnd.a.getAllHero()) {
            totalDamage += hero.performance.damage;
        }
        return totalDamage;
    }

    private static long getTotalHeal(PlayPO play) {
        long totalHeal = 0L;
        for (HeroFighter hero : play.teamEnd.a.getAllHero()) {
            totalHeal += hero.performance.heal;
        }
        return totalHeal;
    }

    private static long getTotalHurt(PlayPO play) {
        long totalHurt = 0L;
        for (HeroFighter hero : play.teamEnd.a.getAllHero()) {
            totalHurt += hero.performance.hurt;
        }
        return totalHurt;
    }



    private static int getHeroStar(GameBody game, PlayPO play, int heroProtoId) {
        if (heroProtoId <= 0) {
            return 0;
        }
        for (BattleHero h : play.a.hs) {
            HeroBaseRow heroTpl = h.hero.getTpl(game.fight.getPlayBuildContext());
            if (heroTpl != null && heroTpl.protoId == heroProtoId) {
                return heroTpl.star;
            }
        }
        return 0;
    }

    private static int getHeroSkillNum(GameBody game, PlayPO play, int heroProtoId, int skillId) {
        if (heroProtoId <= 0) {
            return 0;
        }
        for (BattleHero h : play.a.hs) {
            HeroBaseRow heroTpl = h.hero.getTpl(game.fight.getPlayBuildContext());
            if (heroTpl != null && heroTpl.protoId == heroProtoId) {
                return h.getSkills().contains(skillId) ? 1 : 0;
            }
        }
        return 0;
    }

    private static int getHurtPercent(PlayPO play, int heroProtoId) {
        HeroFighter heroFighter = findHeroFighter(play, heroProtoId);
        if (heroFighter == null) {
            return 0;
        }
        double hurt = heroFighter.performance.hurt;
        double total = 0;
        for (Fighter fixFighter : play.teamEnd.a.getFixFighters()) {
            total += fixFighter.performance.hurt;
        }
        if (total == 0) {
            return 0;
        }
        return (int) (hurt / total * GD.H_100_0);
    }

    private static int getDamagePercent(PlayPO play, int heroProtoId){
        HeroFighter heroFighter = findHeroFighter(play, heroProtoId);
        if (heroFighter == null) {
            return 0;
        }
        double hurt = heroFighter.performance.damage;
        double total = 0;
        for (Fighter fixFighter : play.teamEnd.a.getFixFighters()) {
            total += fixFighter.performance.damage;
        }
        if (total == 0) {
            return 0;
        }
        return (int) (hurt / total * GD.H_100_0);
    }

    private static int getHealPercent(PlayPO play, int heroProtoId){
        HeroFighter heroFighter = findHeroFighter(play, heroProtoId);
        if (heroFighter == null) {
            return 0;
        }
        double hurt = heroFighter.performance.heal;
        double total = 0;
        for (Fighter fixFighter : play.teamEnd.a.getFixFighters()) {
            total += fixFighter.performance.heal;
        }
        if (total == 0) {
            return 0;
        }
        return (int) (hurt / total * GD.H_100_0);
    }

    private static int getKillNum(PlayPO play, int heroProtoId){
        HeroFighter heroFighter = findHeroFighter(play, heroProtoId);
        if (heroFighter == null) {
            return 0;
        }
        return heroFighter.performance.kill;
    }

    private static int getDeadNum(PlayPO play, int heroProtoId){
        HeroFighter heroFighter = findHeroFighter(play, heroProtoId);
        if (heroFighter == null) {
            return 0;
        }
        return heroFighter.performance.dead;
    }

    private static int getPosHeroNum(PlayPO play){
        return play.a.hs.size();
    }

    private static int getTeamRoundKill(PlayPO play, int round) {
        int totalKill = 0;
        for (Fighter fixFighter : play.teamEnd.a.getFixFighters()) {
            for (int i = 1; i <= round; i++) {
                FighterPerformance.RoundPerformance data = fixFighter.performance.getRoundPerformance(i);
                if (data != null) {
                    totalKill += data.kill;
                }
            }
        }
        return totalKill;
    }

    private static int getBuffAddNum(PlayPO play, int heroProtoId, int buffProtoId) {
        int total = 0;
        List<HeroFighter> targets = getWeHeroFighters(play, heroProtoId);
        for (Fighter fixFighter : targets) {
            for (int i = 1; i <= play.rounds.size(); i++) {
                FighterPerformance.RoundPerformance data = fixFighter.performance.getRoundPerformance(i);
                if (data != null) {
                    total += data.buffAddMap.getOrDefault(buffProtoId, 0);
                }
            }
        }
        return total;
    }

    private static int getBuffAddNumOneRound(PlayPO play, int heroProtoId, int buffProtoId) {
        int max = 0;
        List<HeroFighter> targets = getWeHeroFighters(play, heroProtoId);
        for (int i = 1; i <= play.rounds.size(); i++) {
            int roundNum = 0;
            for (Fighter fixFighter : targets) {
                FighterPerformance.RoundPerformance data = fixFighter.performance.getRoundPerformance(i);
                if (data != null) {
                    roundNum += data.buffAddMap.getOrDefault(buffProtoId, 0);
                }
            }
            if (roundNum > max) {
                max = roundNum;
            }
        }
        return max;
    }

    private static int getStrikeNum(PlayPO play, int heroProtoId) {
        int total = 0;
        List<HeroFighter> targets = getWeHeroFighters(play, heroProtoId);
        for (Fighter fixFighter : targets) {
            for (int i = 1; i <= play.rounds.size(); i++) {
                FighterPerformance.RoundPerformance data = fixFighter.performance.getRoundPerformance(i);
                if (data != null) {
                    total += data.strike;
                }
            }
        }
        return total;
    }


    private static int getStrikeNumOneRound(PlayPO play, int heroProtoId) {
        int max = 0;
        List<HeroFighter> targets = getWeHeroFighters(play, heroProtoId);
        for (int i = 1; i <= play.rounds.size(); i++) {
            int roundNum = 0;
            for (Fighter fixFighter : targets) {
                FighterPerformance.RoundPerformance data = fixFighter.performance.getRoundPerformance(i);
                if (data != null) {
                    roundNum += data.strike;
                }
            }
            if (roundNum > max) {
                max = roundNum;
            }
        }
        return max;
    }


    private static int getCastSkillNum(PlayPO play, int heroProtoId,int skillProtoId) {
        int total = 0;
        List<HeroFighter> targets = getWeHeroFighters(play, heroProtoId);
        for (Fighter fixFighter : targets) {
            for (int i = 1; i <= play.rounds.size(); i++) {
                FighterPerformance.RoundPerformance data = fixFighter.performance.getRoundPerformance(i);
                if (data != null) {
                    total += data.skillCastMap.getOrDefault(skillProtoId, 0);
                }
            }
        }
        return total;
    }

    private static int getFirstKillRowNum(PlayPO play, int heroProtoId, int param) {
        HeroFighter heroFighter = findHeroFighter(play, heroProtoId);
        if (heroFighter == null) {
            return 0;
        }
        int num = 0;
        for (FighterPerformance.FighterProfile profile : heroFighter.performance.killList) {
            if (param == 1 && profile.pos > 3) {
                return num;
            }
            if (param == 2 && profile.pos <= 3) {
                return num;
            }
            num++;
        }
        return num;
    }


    /**
     * 查找我方英雄
     * @param play
     * @param heroProtoId 指定英雄 不填就是全部英雄
     * @return
     */
    private static List<HeroFighter> getWeHeroFighters(PlayPO play, int heroProtoId) {
        List<HeroFighter> result = new ArrayList<>();
        if (heroProtoId <= 0) {
            result.addAll(play.teamEnd.a.getAllHero());
        } else {
            for (HeroFighter allHero : play.teamEnd.a.getAllHero()) {
                if (allHero.protoId == heroProtoId) {
                    result.add(allHero);
                    break;
                }
            }
        }
        return result;
    }


    private static HeroFighter findHeroFighter(PlayPO play, int heroProtoId) {
        if (heroProtoId <= 0) {
            return null;
        }
        for (HeroFighter allHero : play.teamEnd.a.getAllHero()) {
            if (allHero.protoId == heroProtoId) {
                return allHero;
            }
        }
        return null;
    }


}
